
ifndef PUP_BUILD_ENVIRONMENT

debug:  export PUP_BUILD_ENVIRONMENT := debug
prod:   export PUP_BUILD_ENVIRONMENT := prod
clean:  export PUP_BUILD_ENVIRONMENT := debug

PUP_PREVIOUS_BUILD_ENV=`cat .PUP_last_build_env 2>/dev/null`

debug prod:
	@touch .PUP_last_build_env
	@if test "$(PUP_PREVIOUS_BUILD_ENV)" = "$(PUP_BUILD_ENVIRONMENT)"; then \
    make all; \
	else \
	  echo $(PUP_BUILD_ENVIRONMENT) > .PUP_last_build_env; \
	  make clean; \
	  make all $(MAKEFLAGS); \
	fi

clean:
	@make clean

.PHONY: debug prod clean

else

# Environment
CC         = gcc
EXEC_DIR   = bin
OBJECT_DIR = build

# Compilation Flags
COMMON_COMPILE_FLAGS := -msse4.2 -std=c99 -ggdb3 -pipe -pthread -Wall -Wextra -Werror \
                        -Ix_attest/include -Iinclude/pup
DEV_COMPILE_FLAGS    := $(COMMON_COMPILE_FLAGS)
PROD_COMPILE_FLAGS   := $(COMMON_COMPILE_FLAGS) -O3 -fno-strict-aliasing

# Callable variables (i.e. functions)
find_in_dir_with_suffix = $(foreach dir,$(1),$(shell find $(dir) -name "*.$(2)"))
dependencies = -MMD -MF $(1:.o=.d) -MT $(1)
dev_compile  = $(CC) $(DEV_COMPILE_FLAGS) $(call dependencies,$(OBJECT_DIR)/$(2)) \
               -c $(1) -o $(OBJECT_DIR)/$(2)
prod_compile = $(CC) $(PROD_COMPILE_FLAGS) $(call dependencies,$(OBJECT_DIR)/$(2)) \
               -c $(1) -o $(OBJECT_DIR)/$(2)
echo_and_execute = echo $(1); $(1)

# Main Source Files
MAIN_SOURCE_ROOT_DIR := src
MAIN_IMPL_FILES   := $(call find_in_dir_with_suffix,$(MAIN_SOURCE_ROOT_DIR),c)

# Main Build Targets
MAIN_OBJECTS = $(addprefix $(OBJECT_DIR)/main/,$(subst src/,,$(MAIN_IMPL_FILES:.c=.o)))
MAIN_OBJECT_DIRS = $(sort $(dir $(MAIN_OBJECTS)))
MAIN_TARGET_LIBRARY := $(EXEC_DIR)/libpup.a

# Test
ATTEST_DIR := x_attest
ATTEST_LIB := $(ATTEST_DIR)/lib/libattest.a
TEST_SOURCE_ROOT_DIR := test
TEST_IMPL_FILES := $(call find_in_dir_with_suffix,$(TEST_SOURCE_ROOT_DIR),c)
TEST_OBJECTS := $(addprefix $(OBJECT_DIR)/,$(TEST_IMPL_FILES:.c=.o))
TEST_OBJECT_DIRS := $(sort $(dir $(TEST_OBJECTS)))
TEST_EXE := $(EXEC_DIR)/tests

all: run_test $(MAIN_TARGET_LIBRARY) main_init 

clean:
	rm -rf $(EXEC_DIR)/* $(OBJECT_DIR)/*

main_init:
	@mkdir -p $(MAIN_OBJECT_DIRS) $(TEST_OBJECT_DIRS)

$(MAIN_TARGET_LIBRARY): $(MAIN_OBJECTS)
	@mkdir -p $(dir $(MAIN_TARGET_LIBRARY))
	ar rcs $(MAIN_TARGET_LIBRARY) $(MAIN_OBJECTS)

$(OBJECT_DIR)/main/%.o: Makefile | main_init
	@if test "$(PUP_BUILD_ENVIRONMENT)" = "debug"; then \
    $(call echo_and_execute,$(call dev_compile,src/$*.c,main/$*.o)); \
	else \
    $(call echo_and_execute,$(call prod_compile,src/$*.c,main/$*.o)); \
  fi

$(OBJECT_DIR)/test/%.o: $(MAIN_TARGET_LIBRARY)
	@if test "$(PUP_BUILD_ENVIRONMENT)" = "debug"; then \
    $(call echo_and_execute,$(call dev_compile,test/$*.c,test/$*.o)); \
	else \
    $(call echo_and_execute,$(call prod_compile,test/$*.c,test/$*.o)); \
  fi

$(ATTEST_LIB): | $(MAIN_TARGET_LIBRARY)
	@cd $(ATTEST_DIR); make; cd -

$(TEST_EXE): $(TEST_OBJECTS) $(ATTEST_LIB)
	$(CC) $(PROD_COMPILE_FLAGS) -Lbin $(TEST_OBJECTS) $(ATTEST_LIB) -o $(TEST_EXE) -lboost_timer-mt -lboost_system-mt -lpup

run_test: $(TEST_EXE)
	./$(TEST_EXE)

%.d:

.PHONY: all clean main_init run_test

define depends_include_template
-include $(1)

endef

$(eval $(foreach dep,$(wildcard $(MAIN_OBJECTS:.o=.d)),$(call depends_include_template,$(dep))))

endif

